package com.modulemanager

import android.net.Uri
import android.util.Log
import com.libbase.Weak
import com.libservice.RouteCompletion
import com.libservice.RouteResult
import com.modulemanager.models.BuildModel
import com.modulemanager.models.PageConfig
import com.modulemanager.module.Module
import com.modulemanager.module.ModuleInterface
import com.modulemanager.page.FlutterPage
import com.modulemanager.page.PageInterface

class ModuleManager private constructor() {
    interface DataSource {
        val jsonPath: String

        fun getLookupKeyForAsset(asset: String, packageName: String) : String
    }

    companion object {
        private const val TAG: String = "ModuleManager"

        val instance: ModuleManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            ModuleManager()
        }
    }

    var dataSource: DataSource? by Weak()

    private val moduleBuilders: MutableMap<String, ModuleInterface.Builder> = mutableMapOf()

    private val modules: MutableList<ModuleInterface> = ArrayList()

    fun register(builder: ModuleInterface.Builder) {
        moduleBuilders[builder.type] = builder
    }

    fun register(moduleType: String, pageBuilders: MutableList<PageInterface.Builder>) {
        register(object : ModuleInterface.Builder {
            override val type: String = moduleType

            override fun build(id: String?): ModuleInterface {
                return Module(type, id, pageBuilders)
            }
        })
    }

    fun flutterPageBuilder(
        pageType: String,
        flutterPageConfig: PageConfig?
    ): PageInterface.Builder {
        return object : PageInterface.Builder {
            override val type: String = pageType

            override fun build(
                module: ModuleInterface,
                id: String?,
                config: PageConfig?,
                params: Map<String, Any>?,
                hash: String?
            ): PageInterface {
                return FlutterPage(
                    module, type, id,
                    config ?: flutterPageConfig,
                    params, hash
                )
            }
        }
    }

    fun module(type: String, id: String?): ModuleInterface? {
        var module = modules.firstOrNull { module -> module.type == type && module.id == id }
        if (module != null) {
            return module
        }

        module = moduleBuilders[type]?.build(id)
        if (module != null) {
            modules.add(module)
        }

        return module
    }

    fun removeModule(type: String, id: String?) {
        modules.removeAll { it.type == type && it.id == id }

        Log.i(TAG, "Remove module type: ${type}, id: ${id ?: "null"}")
        Log.i(TAG, "Module count: ${modules.size}")
    }

    fun page(
        url: String,
        config: PageConfig? = null,
        params: Map<String, Any>? = null
    ): PageInterface? {
        return page(Uri.parse(url), config, params)
    }

    fun page(
        uri: Uri,
        config: PageConfig? = null,
        params: Map<String, Any>? = null
    ): PageInterface? {
        return BuildModel.build(uri, params)?.let {
            if (it.host == "page") page(
                it.moduleType,
                it.moduleId,
                it.type,
                it.id,
                config,
                it.params,
                it.hash
            ) else null
        }
    }

    fun page(
        moduleType: String,
        moduleId: String?,
        pageType: String?,
        pageId: String?,
        config: PageConfig?,
        params: Map<String, Any>? = null,
        hash: String? = null
    ): PageInterface? {
        return module(moduleType, moduleId)?.page(pageType, pageId, config, params, hash)
    }

    fun handle(url: String, params: Map<String, Any>?, completion: RouteCompletion?) {
        handle(Uri.parse(url), params, completion)
    }

    fun handle(uri: Uri, params: Map<String, Any>?, completion: RouteCompletion?) {
        val handlerBuildModel = BuildModel.build(uri, params)
        if (handlerBuildModel == null) {
            completion?.let { it(RouteResult.ERROR, null) }
            return
        }

        if (handlerBuildModel.host != "handler") {
            completion?.let { it(RouteResult.ERROR, null) }
            return
        }

        val module = module(handlerBuildModel.moduleType, handlerBuildModel.moduleId)
        if (module == null) {
            completion?.let { it(RouteResult.ERROR, null) }
            return
        }

        module.handle(handlerBuildModel.type, handlerBuildModel.params, completion)
    }
}